let helpers = require('./helpers')

let TRUE = x => {
    return y => {
        return x;
    }
}

let FALSE = x => {
    return y => {
        return y;
    }
}

let ZERO = p => {
    return x => {
        return x;
    }
}

let ONE = p => {
    return x => {
        return p(x);
    }
}

let TWO = p => {
    return x => {
        return p(p(x));
    }
}

function toInt(f) {
    return f(n => {
        return n + 1
    })(0);
}

let INCREMENT = n => {
    return p => {
        return x => {
            return p(
                n(p)(x)
            );
        }
    }
}

// let THREE = INCREMENT(TWO);
// console.log(helpers.toInt(THREE));

// SLIDE = -> p { PAIR[RIGHT[p]][INCREMENT[RIGHT[p]]] }
function SLIDE(p) {
    return PAIR(
        RIGHT(p)
    )(
        INCREMENT(RIGHT(p))
    );
}

// let s = SLIDE(PAIR(ONE)(ONE));
// console.dir(toInt(RIGHT(s)));

function DECREMENT(n) {
    return LEFT(
        n(SLIDE)(PAIR(ZERO)(ZERO))
    );
}

// let a = DECREMENT(TWO);
// console.dir(toInt(a));

let ADD = (m) => {
    return (n) => {
        return n(INCREMENT)(m);
    }
}
// let b = ADD(TWO)(ONE);
// console.dir(toInt(b));

let SUBTRACT = (m) => {
    return (n) => {
        return n(DECREMENT)(m);
    }
}
// let c = SUBTRACT(TWO)(ONE);
// console.dir(toInt(c));

function MULTIPLY(m) {
    return function (n) {
        return n(ADD(m))(ZERO);
    }
}

// let c = MULTIPLY(TWO)(TWO);
// console.dir(toInt(c));

// function POWER(m) {
//     return function (n) {
//         return n(MULTIPLY(m))(ONE);
//     }
// }

let IS_ZERO = (n) => {
    return n(x => {
        return FALSE;
    })(
        TRUE
    );
}
// let isz = IS_ZERO(TWO);
// console.dir(isz);

let IS_LESS_OR_EQUAL = m => {
    return n => {
        return IS_ZERO(SUBTRACT(m)(n))
    }
}
// console.dir(IS_LESS_OR_EQUAL(TWO)(ONE));

let IF = func => {
    return x => {
        return y => {
            return func(x)(y);
        }
    }
}

// pairs
let PAIR = x => {
    return y => {
        return f => {
            return f(x, y);
        }
    }
}

let LEFT = (p) => {
    return p(function (x) {
        return function (y) {
            return x;
        }
    })
}

let RIGHT = p => {
    return p(x => {
        return y => {
            return y;
        }
    })
}

// let p = PAIR(ONE)(TWO);
// console.dir(
//     helpers.toInt(RIGHT(p))
// );

// Y = -> f { -> x { f[x[x]] }[-> x { f[x[x]] }] }
let Y = f => {
    return (x => {
        return f(x(x))
    })(x => {
        return f(x(x))
    })
}

let k = Y(x => x);
console.dir(k);

// Z = -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }
let Z = f => {
    return (x => {

        f(y => {
            return x(x)(y)
        })

    })(x => {

        f(y => {
            return x(x)(y)
        });

    });
}

let MOD =
    Z(f => {
        return m => {
            return n => {

                return IF(IS_LESS_OR_EQUAL(n)(m))(
                    x => {
                        return f(SUBTRACT(m)(n))(n)(x)
                    }
                )(m)

            }
        }
    })

// let m = MOD(TWO)(TWO)
// console.dir(toInt(m))

// let DIV = function () {
//     return Z(function (f) {
//         return function (m) {
//             return function (n) {
//                 return IF(IS_LESS_OR_EQUAL(n)(m))(
//                     function (x) {
//                         return INCREMENT(f(SUBTRACT(m)(n))(n))(x)
//                     }
//                 )
//             }
//         }
//     })
// };
//
// // lists
//
// let EMPTY = PAIR(TRUE)(TRUE);
//
// let UNSHIFT = function (l) {
//     return function (x) {
//         return PAIR(FALSE)(PAIR(x)(l));
//     }
// };
//
// let IS_EMPTY = LEFT;
//
// let FIRST = function (l) {
//     return LEFT(RIGHT(l))
// };
//
// let REST = function (l) {
//     return l(RIGHT(RIGHT(l)));
// };
//
// let RANGE = function () {
//     return Z(function (f) {
//         return function (m) {
//             return function (n) {
//                 return IF(IS_LESS_OR_EQUAL(m)(n))(
//                     function (x) {
//                         return UNSHIFT(f(INCREMENT(m))(n))(m)(x)
//                     }
//                 )
//             }
//         }
//     });
// };
//
// let FLOD = function () {
//     return Z(function (f) {
//         return function (l) {
//             return function (x) {
//                 return function (g) {
//                     IF(IS_EMPTY(l))(x)(function (y) {
//                         return g(f(REST(l))(x)(g))(FIRST(l))(y);
//                     })
//                 }
//             }
//         }
//     })
// };
//
// let MAP = function (k) {
//     return function (f) {
//         return FLOD(k)(EMPTY)(function (l) {
//             return function (x) {
//                 return UNSHIFT(l)(f(x))
//             }
//         });
//     }
// };
//
// let PUSH = function (l) {
//     return function (x) {
//         return FLOD(l)(UNSHIFT(EMPTY)(x))(UNSHIFT)
//     }
// };
//
// // strings
//
// let TEN = MULTIPLY(ONE)(ONE);

